package com.dai.rpc.netty.codec;

import com.dai.rpc.compress.Compress;
import com.dai.rpc.constant.CompressTypeEnum;
import com.dai.rpc.constant.MyRpcConstants;
import com.dai.rpc.constant.SerializationTypeEnum;
import com.dai.rpc.exceptions.MyRpcException;
import com.dai.rpc.message.RpcMessage;
import com.dai.rpc.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicInteger;

public class MyRpcEncoder extends MessageToByteEncoder<RpcMessage> {


    private static final AtomicInteger ATOMICINTEGER = new AtomicInteger(0);

    /**
     *  每一次发送数据都会经过编码器
     *  编码器职责：按照协议自定义数据
     * @param channelHandlerContext
     * @param rpcMessage
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, RpcMessage rpcMessage, ByteBuf byteBuf) throws Exception {

        /**
         * 1. 4B  magic code（魔法数）
         * 2. 1B version（版本）
         * 3. 4B full length（整个报文消息长度）
         * 4. 1B messageType（消息类型）
         * 5. 1B codec（序列化类型）
         * 6. 1B compress（压缩类型）
         * 7. 4B  requestId（请求的Id）
         * 8. body（object类型数据）
         */

        byte codecType = rpcMessage.getCodec();
        byte compressType = rpcMessage.getCompress();
        byte messageType = rpcMessage.getMessageType();

        byteBuf.writeBytes(MyRpcConstants.RPC_MAGIC_TABLE);
        byteBuf.writeByte(MyRpcConstants.RPC_VERSION);
        // 报文消息长度预留
        // 无参writerIndex作用：返回当前写下标。有参writerIndex作用：更改写下标
        byteBuf.writerIndex(byteBuf.writerIndex() + 4);
        byteBuf.writeByte(messageType);
        byteBuf.writeByte(codecType);
        byteBuf.writeByte(compressType);
        // 使用原子类保证请求id唯一
        int requestId = MyRpcEncoder.ATOMICINTEGER.getAndIncrement();
        byteBuf.writeInt(requestId);
        // 写入数据
        int fullLength = MyRpcConstants.TOTAL_LENGTH;
        byte[] bodyBuff = null;
        // 先要序列化
        Serializer serializer = loadSerializer(codecType);
        bodyBuff = serializer.serialize(rpcMessage.getData());
        // 压缩
        Compress compress  = loadCompress(compressType);
        bodyBuff = compress.compress(bodyBuff);
        fullLength += bodyBuff.length;

        byteBuf.writeBytes(bodyBuff);
        int writeIndex = byteBuf.writerIndex();
        // 更改写下标将fullLength写入之前预留的位置
        byteBuf.writerIndex(writeIndex - fullLength + MyRpcConstants.RPC_MAGIC_TABLE.length + 1);
        byteBuf.writeInt(fullLength);
        // 更改写下标
        byteBuf.writerIndex(writeIndex);

    }

    private Serializer loadSerializer(byte codecType) {
        // 根据spi技术找到需要加载的实现类
        String serializerName = SerializationTypeEnum.getName(codecType);
        ServiceLoader<Serializer> load = ServiceLoader.load(Serializer.class);
        for (Serializer serializer : load) {
            if(serializer.name().equals(serializerName)) return serializer;
        }
        throw new MyRpcException("未找到对应的序列化类型");
    }

    private Compress loadCompress(byte compressType) {
        // 根据spi技术找到需要加载的实现类
        String compressName = CompressTypeEnum.getName(compressType);
        ServiceLoader<Compress> load = ServiceLoader.load(Compress.class);
        for (Compress compress : load) {
            if(compress.name().equals(compressName)) return compress;
        }
        throw new  MyRpcException("未找到对应的压缩类型");
    }


}
